package jwt

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/golang-jwt/jwt/v5"
	"time"
)

var (
	jwtSecret          = []byte("98uu7h66te4ss12xbx8zoo9pq77sdf53f0") // 替换为你自己的密钥
	accessTokenExpire  = time.Duration(360)                           // 访问Token过期时间
	refreshTokenExpire = time.Duration(720)                           // 刷新Token过期时间
)

type JwtConfig struct {
	AccessTokenExpireHours  int      `mapstructure:"access_token_expire_hours"`  // JWT Token令牌有效时长
	RefreshTokenExpireHours int      `mapstructure:"refresh_token_expire_hours"` //JWT 刷新令牌有效时长
	SecretKey               string   `mapstructure:"secret_key"`                 // 密钥(加密盐)
	NoAuthUrls              []string `mapstructure:"no_auth_urls"`               // 不需要 Token令牌的请求URL
}

var JwtConf *JwtConfig

func InitJwtConfig(jwtConf *JwtConfig) {
	JwtConf = jwtConf
	jwtSecret = []byte(jwtConf.SecretKey)
	accessTokenExpire = time.Duration(jwtConf.AccessTokenExpireHours)
	refreshTokenExpire = time.Duration(jwtConf.RefreshTokenExpireHours)
}

// Claims 数据载体
type Claims struct {
	UserId string `json:"user_id"`
	jwt.RegisteredClaims
}

// GenerateTokens 生成访问Token和刷新Token
func GenerateTokens(userId string) (accessToken, refreshToken string, err error) {
	now := time.Now()

	// 生成访问Token
	accessClaims := Claims{
		UserId: userId,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(accessTokenExpire * time.Hour)), // Token 过期时间
			IssuedAt:  jwt.NewNumericDate(now),                                    // Token 签发时间
			NotBefore: jwt.NewNumericDate(now),                                    // Token 在此时间之前不可用
			Issuer:    "go-web-admin",                                             // 签发者（Issuer）
		},
	}
	accessToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims).SignedString(jwtSecret)
	if err != nil {
		return "", "", err
	}

	// 生成刷新Token
	refreshClaims := Claims{
		UserId: userId,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(refreshTokenExpire * time.Hour)), // 过期时间
			IssuedAt:  jwt.NewNumericDate(now),                                     // 发放时间
			NotBefore: jwt.NewNumericDate(now),
			Issuer:    "go-web-admin",
		},
	}
	refreshToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims).SignedString(jwtSecret)
	if err != nil {
		return "", "", err
	}

	return accessToken, refreshToken, nil
}

// RefreshAccessToken 使用刷新Token获取新的访问Token
func RefreshAccessToken(refreshToken string) (newAccessToken string, err error) {
	// 解析刷新Token但不验证过期时间
	token, err := jwt.ParseWithClaims(refreshToken, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return jwtSecret, nil
	}, jwt.WithLeeway(5*time.Minute))

	if err != nil {
		return "", err
	}

	if claims, ok := token.Claims.(*Claims); ok {
		// 验证刷新Token是否即将过期（例如剩余时间小于1天）
		if claims.ExpiresAt.Sub(time.Now()) < 24*time.Hour {
			return "", errors.New("refresh token is about to expire")
		}

		// 生成新的访问Token
		return GenerateAccessToken(claims.UserId)
	}

	return "", errors.New("invalid refresh token")
}

// GenerateAccessToken 仅生成访问Token
func GenerateAccessToken(userId string) (string, error) {
	now := time.Now()
	claims := Claims{
		UserId: userId,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(accessTokenExpire * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(now),
			NotBefore: jwt.NewNumericDate(now),
			Issuer:    "your-app-name",
		},
	}
	return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(jwtSecret)
}

// ParseToken 解析Token（通用方法）
func ParseToken(tokenString string) (*Claims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return jwtSecret, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*Claims); ok && token.Valid {
		return claims, nil
	}

	return nil, err
}

func main() {

	// 生成访问Token和刷新Token
	accessToken, refreshToken, err := GenerateTokens("123")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("access:", accessToken)
	fmt.Println("refree:", refreshToken)

	// 使用刷新Token获取新的访问Token
	//newAcceToken, err := RefreshAccessToken(refreshToken)
	//if err != nil {
	//	fmt.Println(err)
	//}
	//fmt.Println("newAcceToken:", newAcceToken)

	// 解析Token（通用方法）
	//accessToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjY1MTMwNzgwMjgzODk5OTA0IiwiaXNzIjoiZ28td2ViLWFkbWluIiwiZXhwIjoxNzUzMDg0ODAxLCJuYmYiOjE3NTMwODQ4MDEsImlhdCI6MTc1MzA4NDgwMX0.7OyMAOdm3iG1eqff6YaDnzwkbh5ssOiX0SxLhPzY_-0"
	claims, err := ParseToken(accessToken)
	if err != nil {
		fmt.Println(err)
	}
	marshal, err := json.Marshal(claims)
	fmt.Println("claims:", string(marshal))

}
